#include <gnome.h>
#include <gr_graph_fun.h>
#include <gr_node.h>
#include <assert.h>

#include "graph.h"

GraphFrame *
init_graph(int width,int height)
{
	GraphFrame *graph = NULL;
	graph = create_current_graph();
	set_dimensions(graph, width, height);
	set_show_vertex_weight(graph, False);
	set_show_edge_weight(graph, False);
	set_shape(graph, BOX);
	set_dim_vertex(graph,BSIDE,HSIDE);
	set_scale(graph, 0);
	set_flag_draw(graph, True);
	set_num_vertex(graph, 0);
	set_num_ver_group(graph, 0);
	set_count_vertex(graph,0);
	set_method_tree(graph, BFS_TREE);
	set_graph_dir(graph, DIRECTED);
	return graph;
}

graph_options *
init_options(void) {
	graph_options *opts = NULL;
	GdkColor black = { 0x0000, 0x00, 0x00, 0x00 };
	
	if(!(opts = (graph_options *)malloc(sizeof(graph_options))))
		perror_exit("%s:%d ",__FILE__,__LINE__);
	
	if(!(opts->node_color = (GdkColor *)malloc(sizeof(GdkColor))))
		perror_exit("%s:%d",__FILE__,__LINE__);

	if(!(opts->edge_color = (GdkColor *)malloc(sizeof(GdkColor))))
		perror_exit("%s:%d",__FILE__,__LINE__);

	if(!(opts->label_color = (GdkColor *)malloc(sizeof(GdkColor))))
		perror_exit("%s:%d",__FILE__,__LINE__);
	
	opts->label_font = gdk_font_load("-*-clean-medium-r-normal-*-8-*-*-*-*-*-*");
	gdk_font_ref(opts->label_font);
	opts->show_label  = 0;

	memcpy(opts->node_color,&black,sizeof(GdkColor));
	memcpy(opts->edge_color,&black,sizeof(GdkColor));
	memcpy(opts->label_color,&black,sizeof(GdkColor));

	return opts;
}

static gint
item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data)
{
	static double x, y;
	double new_x, new_y;
	GdkCursor *fleur;
	static int dragging;
	static int zooming;
	double item_x, item_y;
	static double ppu;

	GnomeCanvas *canvas = (GnomeCanvas *)data;
	
	item_x = event->button.x;
	item_y = event->button.y;
	gnome_canvas_item_w2i(item->parent, &item_x, &item_y);

	switch (event->type)  {
	case GDK_BUTTON_PRESS:
		switch(event->button.button) {
		case 2:
			if (event->button.state & GDK_SHIFT_MASK) {
				//gtk_object_destroy(GTK_OBJECT(item));
			}
			else {
				x = item_x;
				y = item_y;
				fleur = gdk_cursor_new(GDK_FLEUR);
				gnome_canvas_item_grab(item,
						GDK_POINTER_MOTION_MASK | 
						GDK_BUTTON_RELEASE_MASK,
						fleur,
						event->button.time);
				gdk_cursor_destroy(fleur);
				dragging = TRUE;
			}
			break;
		case 3:
			y = item_y;
			x = item_x; 
			//zooming = TRUE;
			zooming = FALSE;
			ppu = canvas->pixels_per_unit;
			break;
		default:
			break;
		}
		break;

	case GDK_MOTION_NOTIFY:
		if (dragging && (event->motion.state & GDK_BUTTON2_MASK)) {
			new_x = item_x;
			new_y = item_y;
			gnome_canvas_item_move(item, new_x - x, new_y - y);
			x = new_x;
			y = new_y;
		}
		if (zooming && (event->motion.state & GDK_BUTTON3_MASK)) {
			new_y = item_y;
			
			if(new_y > y)
				fleur = gdk_cursor_new(GDK_SB_DOWN_ARROW);
			if(new_y == y)
				fleur = gdk_cursor_new(GDK_SB_V_DOUBLE_ARROW);
			if(new_y < y)
				fleur = gdk_cursor_new(GDK_SB_UP_ARROW);
			gnome_canvas_item_grab(item,
					GDK_POINTER_MOTION_MASK | 
						GDK_BUTTON_RELEASE_MASK,fleur,event->button.time);
			gdk_cursor_destroy(fleur);
			
			/* scale to a number between 0.1 and 2 */
			new_y = (((new_y - y)/150.0)+1.0);
			if(new_y < 0.1)
				new_y = 0.1;
			if(new_y > 2.0)
				new_y = 2.0;

			/* new pixels/unit is new_y% of old pixels/unit */
			gnome_canvas_set_pixels_per_unit(canvas,ppu*new_y);   // scale
			gnome_canvas_item_move(item, x, y);                   // recenter
		}
		break;

	case GDK_BUTTON_RELEASE:
		gnome_canvas_item_ungrab(item, event->button.time);
		dragging = FALSE;
		zooming = FALSE;
      break;

	default:
		break;
	}
	return FALSE;
}


static gint
node_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data)
{
	GnomeCanvas *canvas = (GnomeCanvas *)data;

	switch (event->type)  {
	case GDK_BUTTON_PRESS:
		switch(event->button.button) {
		case 1:
			fprintf(stderr,"button 1 press on node\n");
			break;
		}
		break;
	case GDK_MOTION_NOTIFY:
		fprintf(stderr,"motion notify\n");
		break;
	case GDK_BUTTON_RELEASE:
		fprintf(stderr,"button release\n");
      break;
	default:
		break;
	}
	return FALSE;
}

static void
setup_item(GnomeCanvasGroup *canvas, GnomeCanvasItem *item)
{
	gtk_signal_connect(GTK_OBJECT(item), "event",
			(GtkSignalFunc) item_event,
			(void *)canvas);
}

static void
setup_node(GnomeCanvasGroup *canvas, GnomeCanvasItem *item)
{
	gtk_signal_connect(GTK_OBJECT(item), "event",
			(GtkSignalFunc) node_event,
			(void *)canvas);
}

void
draw_label(GnomeCanvasGroup *graph,char *label,double x, double y, graph_options *opts) {
	
	if(opts->show_label == 0)
		return;

	gnome_canvas_item_new(graph,
			GNOME_TYPE_CANVAS_TEXT,
			"x", x, "y", y,
			"text", label,
			"justification", GTK_JUSTIFY_LEFT,
			"anchor", GTK_ANCHOR_SOUTH_WEST,
			"fill_color_gdk", opts->label_color,
			"font_gdk", &opts->label_font,
			NULL);

}

GnomeCanvasGroup *
draw_node(GnomeCanvasGroup *graph,PNODE node, graph_options *opts) {
	GnomeCanvasGroup *node_group;
	double x,y;

	assert(graph != NULL);
	assert(node != NULL);

	x = (double)get_x(node);
	y = (double)get_y(node);

	node_group = GNOME_CANVAS_GROUP(
		gnome_canvas_item_new(graph,
			gnome_canvas_group_get_type(),"x",x,"y",y,NULL));

	gnome_canvas_item_new(graph,
		gnome_canvas_ellipse_get_type(),
			"x1",x - 3.0,
			"y1",y - 3.0,
			"x2",x + 3.0,
			"y2",y + 3.0,
			"fill_color_gdk", opts->node_color,
			NULL);

	draw_label(graph,node->label,x,y,opts);
	setup_node(graph,node_group);

	return node_group;
}

GnomeCanvasGroup *
draw_edge(GnomeCanvasGroup *graph,PEDGE edge, graph_options *opts) {
	PNODE source,target;
	GnomeCanvasPoints *points;

	GnomeCanvasPathDef *pd;
	ArtBpath *bpath;

	double x0,y0,x1,y1,x2,y2;
	double ix0,iy0,ix1,iy1;

	assert(graph != NULL);
	assert(edge  != NULL);
	
	source = get_from_vertex(edge);
	target = get_to_vertex(edge);
	
	points = gnome_canvas_points_new(2);

	
	x0 = (double)get_x(source);
	y0 = (double)get_y(source);
	x2 = (double)get_x(target);
	y2 = (double)get_y(target);
	
	ix0 = x2;
	iy0 = y0;
	ix1 = x0 + (x2-x0)/2;
	iy1 = y0 + (y2-y0)/2;
	
	x1 = ix1 + (ix1-ix0)/2;
	y1 = iy1 + (iy1-iy0)/2;
	
	/*
	pd = gnome_canvas_path_def_new_sized(3);
	gnome_canvas_path_def_moveto (pd,x0,y0);
	gnome_canvas_path_def_curveto (pd,x0,y0,x1,y1,x2,y2);
	gnome_canvas_item_new (graph,
			gnome_canvas_bpath_get_type (),
			"bpath", pd,
			"fill_color_rgba", 0x000000ff,
			NULL);
	*/

	points->coords[0] = x0;
	points->coords[1] = y0;
	points->coords[2] = x2;
	points->coords[3] = y2;
	//points->coords[2] = ix0 + (ix0-ix1)/2;
	//points->coords[3] = iy0 + (iy0-iy1)/2;
	
	gnome_canvas_item_new(graph,
			gnome_canvas_line_get_type(),
			"points",points,
			"fill_color_gdk", opts->edge_color,
			"last_arrowhead", TRUE,
			"arrow_shape_a", 8.0,
			"arrow_shape_b", 12.0,
			"arrow_shape_c", 8.0,
			"spline_steps", 2,
			"smooth", TRUE,
			NULL);
	
	gnome_canvas_points_unref(points);	
}

void
draw_graph(GnomeCanvas *canvas,GraphFrame *graph, graph_options *opts)
{
	LNode_type *vertices,*edges,*ptr;
	PNODE node;
	PEDGE edge;
	GnomeCanvasGroup *window;

	clear_graph(canvas);
	
	compute_force_directed_positions(graph);
	move_graph_to_given_position(graph,0,0);

	window = GNOME_CANVAS_GROUP(
		gnome_canvas_item_new(gnome_canvas_root(canvas),
			gnome_canvas_group_get_type(),"x",0,"y",0,NULL));
	setup_item(canvas, window);
	
	
	edges = get_list_elements(graph->Hedge);
	for(ptr=edges->back;ptr!=edges;ptr=ptr->back) {
		edge = (PEDGE)ptr->info;
		draw_edge(window,edge,opts);
	}
	vertices = get_list_elements(graph->HV);
	for(ptr=vertices->back;ptr!=vertices;ptr=ptr->back) {
		node = (PNODE)ptr->info;
		draw_node(window,node,opts);
		if(get_x(node) > graph->wp)
			graph->wp = get_x(node);
		if(get_y(node) > graph->hp)
			graph->hp = get_y(node);
	}
	gnome_canvas_set_scroll_region(canvas,0.0,0.0,(double)graph->wp,(double)graph->hp);
}

void
clear_graph(GnomeCanvas *canvas) {
	GnomeCanvasGroup *root;
	root = gnome_canvas_root(canvas);
	g_list_foreach(root->item_list,(gtk_object_destroy),NULL);
}
